﻿
/// <reference path="_namespace.js" />
/// <reference path="GraphicsBase.js" />
/// <reference path="Line.js" />
/// <reference path="../geometry/shapes/Rectangle.js" />

/*****************************************************************************
Ajax library class definition. Kevin Robinson 2006.
Class: ajax.drawing.Graphics

Note: This class is heavily based on work done by Walter Zorn, please see
his copyright notice for his file "wz_jsgraphics.js" at the end of this file.

/*****************************************************************************/

ajax.lang.addNamespace("ajax.drawing");

$imports("ajax.drawing.Line");
$imports("ajax.drawing.Point");
$imports("ajax.geometry.shapes.Rectangle");

//-----------------------------------------------------------------------------
ajax.drawing.Graphics = function(doc, id, width, height) // Constructor
{
    ajax.drawing.Graphics.superConstructor.call(this, doc, width, height);
    
    this.setScale(1);
        
    // Load in the color class
    this.colorType = ajax.lang.safeCall("ajax.drawing.Colour", "getTypes");
    
    // For compatability with the Java equiv
    this.images = {};
    this.addImage = function(src)
    {
        var img = this.doc.createElement("img");
        img.src = src;
        this.images[src] = img;
    }
    
    this.removeImage = function(src)
    {
        this.images[src] = null;
    };
    
    this.setSize  = function(x, y){};
    
    this.setColor = function(colour)
    {///<param name="colour" type="ajax.drawing.Colour"/>
        this.color = colour.toHex();               
    };
        
    /*
	this.setStroke = function(x)
	{
		this.stroke = x;
		if (!(x+1))
		{
			this.drawLine   = ajax.drawing.Graphics.mkLinDott;
			this.mkOv       = ajax.drawing.Graphics.mkOvDott;
			this.drawRect   = ajax.drawing.Graphics.mkRectDott;
		}
		else if (x-1 > 0)
		{
			this.drawLine   = ajax.drawing.Graphics.mkLin2D;
			this.mkOv       = ajax.drawing.Graphics.mkOv2D;
			this.drawRect   = ajax.drawing.Graphics.mkRect;
		}
		else
		{
			this.drawLine   = ajax.drawing.Graphics.mkLin;
			this.mkOv       = ajax.drawing.Graphics.mkOv;
			this.drawRect   = ajax.drawing.Graphics.mkRect;
		}
	};
	*/


	this.setPrintable = function(arg)
	{
		this.printable = arg;
		if (ajax.drawing.Graphics.jg_fast)
		{
			this.mkDiv = ajax.drawing.Graphics.mkDivIe;
			this.htmRpc = arg? ajax.drawing.Graphics.htmPrtRpc : ajax.drawing.Graphics.htmRpc;
		}
		else this.mkDiv = ajax.drawing.Graphics.jg_n4? ajax.drawing.Graphics.mkLyr : arg? ajax.drawing.Graphics.mkDivPrt : ajax.drawing.Graphics.mkDiv;
	};


	this.setFont = function(fam, sz, sty)
	{
		this.ftFam = fam;
		this.ftSz = sz + "px";
		this.ftSty = sty || ajax.drawing.Graphics.Font.PLAIN;
	};


	this.drawPolyline = this.drawPolyLine = function(x, y, s)
	{
		for (var i=0 ; i<x.length-1 ; i++ )
			this.drawLine(x[i], y[i], x[i+1], y[i+1]);
	};
	// For Java Compliance
	this.drawPolyline3 = function(x1, y1, x2, y2, x3, y3)
	{	    
	    this.drawLine(x1, y1, x2, y2);
	    this.drawLine(x2, y2, x3, y3);
	}

	this.fillRect = function(x, y, w, h)
	{	    	    	    
		this.mkDiv(x, y, w, h);
	};

    // Draws an "any shape" polygon.
	this.drawPolygon = function(x, y)
	{
		this.drawPolyline(x, y);
		this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);
	};
	// For Java Compliance
	this.drawPolygon3 = function(x1, y1, x2, y2, x3, y3)
	{
	    // Always clip	    
	    var line1 = new ajax.drawing.Line(new ajax.drawing.Point(x1, y1), new ajax.drawing.Point(x2, y2));
	    var line2 = new ajax.drawing.Line(new ajax.drawing.Point(x2, y2), new ajax.drawing.Point(x3, y3));
	    var line3 = new ajax.drawing.Line(new ajax.drawing.Point(x3, y3), new ajax.drawing.Point(x1, y1));	        
	    	    
	    var w = this.scaleAdjustedWidth;
	    var h = this.scaleAdjustedHeight;
	    
	    if (ajax.drawing.GraphicsBase.clipLine(line1, w, h))
	    //ajax.drawing.GraphicsBase.clipLine(line1, w, h);
	        this.drawLine(line1.pointA.x, line1.pointA.y, line1.pointB.x, line1.pointB.y);
	        
	    if (ajax.drawing.GraphicsBase.clipLine(line2, w, h))
	    //ajax.drawing.GraphicsBase.clipLine(line2, w, h);
	        this.drawLine(line2.pointA.x, line2.pointA.y, line2.pointB.x, line2.pointB.y);
	     
	     if (ajax.drawing.GraphicsBase.clipLine(line3, w, h))
	     //ajax.drawing.GraphicsBase.clipLine(line3, w, h);
	        this.drawLine(line3.pointA.x, line3.pointA.y, line3.pointB.x, line3.pointB.y);
	    
	    //this.drawPolyline3(x1, y1, x2, y2, x3, y3);
	    //this.drawLine(x3, y3, x1, y1);
	};


	this.drawEllipse = this.drawOval = function(x, y, w, h)
	{
		this.mkOv(x, y, w, h);
	};


	this.fillEllipse = this.fillOval = function(left, top, w, h)
	{
		var a = (w -= 1)>>1, b = (h -= 1)>>1,
		wod = (w&1)+1, hod = (h&1)+1,
		cx = left+a, cy = top+b,
		x = 0, y = b,
		ox = 0, oy = b,
		aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
		st = (aa2>>1)*(1-(b<<1)) + bb,
		tt = (bb>>1) - aa2*((b<<1)-1),
		pxl, dw, dh;
		if (w+1) while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - aa4*(y-1);
				pxl = cx-x;
				dw = (x<<1)+wod;
				tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
				dh = oy-y;
				this.mkDiv(pxl, cy-oy, dw, dh);
				this.mkDiv(pxl, cy+y+hod, dw, dh);
				ox = x;
				oy = y;
			}
			else
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
			}
		}
		this.mkDiv(cx-a, cy-oy, w+1, (oy<<1)+hod);
	};

    this.fillArc = function(x, y, width, height, startAngle, arcAngle)
	{
	    while (startAngle < 0)
	        startAngle += 360;
        while (startAngle > 360)
            startAngle -= 360;
        while (arcAngle < 0)
            arcAngle += 360;
        while (arcAngle > 360)
            arcAngle -= 360;
            
	    var iL = x;
	    var iT = y;
	    var iW = width;
	    var iH = height;
	    var fAngA  = startAngle;
	    var fAngZ  = startAngle + arcAngle;
	    
		var a = iW>>1, b = iH>>1,
		iOdds = (iW&1) | ((iH&1) << 16),
		cx = iL+a, cy = iT+b,
		x = 0, y = b, ox = x, oy = y,
		aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,
		st = (aa2>>1)*(1-(b<<1)) + bb2,
		tt = (bb2>>1) - aa2*((b<<1)-1),
		// Vars for radial boundary lines
		xEndA, yEndA, xEndZ, yEndZ,
		iSects = (1 << (Math.floor((fAngA %= 360.0)/180.0) << 3))
				| (2 << (Math.floor((fAngZ %= 360.0)/180.0) << 3))
				| ((fAngA >= fAngZ) << 16),
		aBndA = new Array(b+1), aBndZ = new Array(b+1);
		
		// Set up radial boundary lines
		fAngA *= Math.PI/180.0;
		fAngZ *= Math.PI/180.0;
		xEndA = cx+Math.round(a*Math.cos(fAngA));
		yEndA = cy+Math.round(-b*Math.sin(fAngA));
		this.mkLinVirt(aBndA, cx, cy, xEndA, yEndA);
		xEndZ = cx+Math.round(a*Math.cos(fAngZ));
		yEndZ = cy+Math.round(-b*Math.sin(fAngZ));
		this.mkLinVirt(aBndZ, cx, cy, xEndZ, yEndZ);

		while(y > 0)
		{
			if(st < 0) // Advance x
			{
				st += bb2*((x<<1)+3);
				tt += bb4*(++x);
			}
			else if(tt < 0) // Advance x and y
			{
				st += bb2*((x<<1)+3) - aa4*(y-1);
				ox = x;
				tt += bb4*(++x) - aa2*(((y--)<<1)-3);
				this.mkArcDiv(ox, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
				oy = y;
			}
			else // Advance y
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
				if(y && (aBndA[y] != aBndA[y-1] || aBndZ[y] != aBndZ[y-1]))
				{
					this.mkArcDiv(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
					ox = x;
					oy = y;
				}
			}
		}
		this.mkArcDiv(x, 0, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);
		if(iOdds >> 16) // Odd height
		{
			if(iSects >> 16) // Start-angle > end-angle
			{
				var xl = (yEndA <= cy || yEndZ > cy)? (cx - x) : cx;
				this.mkDiv(xl, cy, x + cx - xl + (iOdds & 0xffff), 1);
			}
			else if((iSects & 0x01) && yEndZ > cy)
				this.mkDiv(cx - x, cy, x, 1);
		}
	};
	
	this.mkLinVirt = ajax.drawing.Graphics.mkLinVirt;

    //-----------------------------------------------------------------------------
    /* fillPolygon method, implemented by Matthieu Haller.
    This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.
    C source of GD 1.8.4 found at http://www.boutell.com/gd/

    THANKS to Kirsten Schulz for the polygon fixes!

    The intersection finding technique of this code could be improved
    by remembering the previous intertersection, and by using the slope.
    That could help to adjust intersections to produce a nice
    interior_extrema. */
    
    // For Java Compliance
    this.fillPolygon3 = function(x1, y1, x2, y2, x3, y3)
    {
        // Always clip	
        /*    
        var line1 = new ajax.drawing.Line(new ajax.drawing.Point(x1, y1), new ajax.drawing.Point(x2, y2));
        var line2 = new ajax.drawing.Line(new ajax.drawing.Point(x2, y2), new ajax.drawing.Point(x3, y3));
        var line3 = new ajax.drawing.Line(new ajax.drawing.Point(x3, y3), new ajax.drawing.Point(x1, y1));	        
        	    
        var w = this.scaleAdjustedWidth;
        var h = this.scaleAdjustedHeight;

        //if (ajax.drawing.GraphicsBase.clipLine(line1, w, h))
        ajax.drawing.GraphicsBase.clipLine(line1, w, h);
            this.drawLine(line1.pointA.x, line1.pointA.y, line1.pointB.x, line1.pointB.y);
            
        //if (ajax.drawing.GraphicsBase.clipLine(line2, w, h))
        ajax.drawing.GraphicsBase.clipLine(line2, w, h);
            this.drawLine(line2.pointA.x, line2.pointA.y, line2.pointB.x, line2.pointB.y);
         
         //if (ajax.drawing.GraphicsBase.clipLine(line3, w, h))
         ajax.drawing.GraphicsBase.clipLine(line3, w, h);
            this.drawLine(line3.pointA.x, line3.pointA.y, line3.pointB.x, line3.pointB.y);
        
        var array_x = [line1.pointA.x, line1.pointB.x, line3.pointA.x];
        var array_y = [line1.pointA.y, line1.pointB.y, line3.pointA.y];
        */
        
        var array_x = [x1, x2, x3];
        var array_y = [y1, y2, y3];
        return this.fillPolygon(array_x, array_y);
    };
    
	this.fillPolygon = function(array_x, array_y)
	{
		var i;
		var y;
		var miny, maxy;
		var x1, y1;
		var x2, y2;
		var ind1, ind2;
		var ints;
        var lineHeight = 1; // KR: just to advertise the fact!
		var n = array_x.length;

		if (!n) return;

        
		miny = array_y[0];
		maxy = array_y[0];
		
		
		for (i = 1; i < n; i++)
		{
			if (array_y[i] < miny)
				miny = array_y[i];

			if (array_y[i] > maxy)
				maxy = array_y[i];
		}
		
		// KR: Constrain these to the bounds
		lineHeight  = 1;
		miny        = Math.max(0, miny)
		maxy        = Math.min(this.scaleAdjustedHeight-1, maxy);
		
		for (y = miny; y <= maxy; y++)
		{
			var polyInts = new Array();
			ints = 0;
			for (i = 0; i < n; i++)
			{
				if (!i)
				{
					ind1 = n-1;
					ind2 = 0;
				}
				else
				{
					ind1 = i-1;
					ind2 = i;
				}
				y1 = array_y[ind1];
				y2 = array_y[ind2];
				if (y1 < y2)
				{
					x1 = array_x[ind1];
					x2 = array_x[ind2];
				}
				else if (y1 > y2)
				{
					y2 = array_y[ind1];
					y1 = array_y[ind2];
					x2 = array_x[ind1];
					x1 = array_x[ind2];
				}
				else continue;

				 // modified 11. 2. 2004 Walter Zorn
				if ((y >= y1) && (y < y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);

				else if ((y == maxy) && (y > y1) && (y <= y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
			}
			polyInts.sort(ajax.drawing.Graphics.integer_compare);			
			for (i = 0; i < ints; i+=2)
				this.mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);
		}
	};


	this.drawString = function(txt, x, y)
	{
		this.htm += '<div style="position:absolute;white-space:nowrap;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'font-family:' +  this.ftFam + ';'+
			'font-size:' + this.ftSz + ';'+
			'color:' + this.color + ';' + this.ftSty + '">'+
			txt +
			'<\/div>';
	};


    /*
    drawStringRect() added by Rick Blommers.
    Allows to specify the size of the text rectangle and to align the
    text both horizontally (e.g. right) and vertically within that rectangle 
    */
	this.drawStringRect = function(txt, x, y, width, halign)
	{
		this.htm += '<div style="position:absolute;overflow:hidden;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:'+width +'px;'+
			'text-align:'+halign+';'+
			'font-family:' +  this.ftFam + ';'+
			'font-size:' + this.ftSz + ';'+
			'color:' + this.color + ';' + this.ftSty + '">'+
			txt +
			'<\/div>';
	};


	this.drawImage = function(img, x, y, w, h, a)
	{
		this.htm += '<div style="position:absolute;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:' +  w + 'px;'+
			'height:' + h + 'px;">'+
			'<img src="' + img.src + '" width="' + w + '" height="' + h + '"' + (a? (' '+a) : '') + '>'+
			'<\/div>';
	};
	
	this.drawImageToRect = function(domImage, destinationRect)
	{
	    var r = ajax.geometry.shapes.Rectangle.cast(destinationRect);
	    this.drawImage(domImage, r.x, r.y, r.width, r.height);
	};
	
	this.drawImageFromRect = function(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, a)
	{
	    // Get existing image width & height
	    var imgH = img.height;
	    var imgW = img.width;	    
	    // calculate image scalers
	    var dsW  = dWidth / sWidth;
	    var dsH  = dHeight / sHeight;
	    // modify the image dimensions
	    imgW = parseInt(imgW * dsW);
	    imgH = parseInt(imgH * dsH);
	    var imgPosX = parseInt( -sx * dsW);
	    var imgPosY = parseInt( -sy * dsH);
	    	    
	    var content = '<div style="position:absolute;overflow:hidden;' +
	        'left:' + dx + 'px;' +
	        'top:' + dy + 'px;' + 
	        'width:' +  dWidth + 'px;'+
			'height:' + dHeight + 'px;">'+
			'<img src="' + img.src + '" width="' + imgW + '" height="' + imgH + '" style="position:absolute;' +
			'left:' + imgPosX + 'px;' +
			'top:' + imgPosY + 'px;" ' + (a? (' '+a) : '') + '>'+
			'<\/div>';
				    
	    this.htm += content;
	}


	this.clear = function(colour)
	{
		this.htm = "";
		if (this.cnv) 
		    this.cnv.innerHTML = this.defhtm;		    
	};

    // mkArcDiv here
    this.mkArcDiv = function(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects)
	{
		var xrDef = cx + x + (iOdds & 0xffff), y2, h = oy - y, xl, xr, w;

		if(!h) h = 1;
		x = cx - x;

		if(iSects & 0xff0000) // Start-angle > end-angle
		{
			y2 = cy - y - h;
			if(iSects & 0x00ff)
			{
				if(iSects & 0x02)
				{
					xl = Math.max(x, aBndZ[y]);
					w = xrDef - xl;
					if(w > 0) this.mkDiv(xl, y2, w, h);
				}
				if(iSects & 0x01)
				{
					xr = Math.min(xrDef, aBndA[y]);
					w = xr - x;
					if(w > 0) this.mkDiv(x, y2, w, h);
				}
			}
			else
				this.mkDiv(x, y2, xrDef - x, h);
			y2 = cy + y + (iOdds >> 16);
			if(iSects & 0xff00)
			{
				if(iSects & 0x0100)
				{
					xl = Math.max(x, aBndA[y]);
					w = xrDef - xl;
					if(w > 0) this.mkDiv(xl, y2, w, h);
				}
				if(iSects & 0x0200)
				{
					xr = Math.min(xrDef, aBndZ[y]);
					w = xr - x;
					if(w > 0) this.mkDiv(x, y2, w, h);
				}
			}
			else
				this.mkDiv(x, y2, xrDef - x, h);
		}
		else
		{
			if(iSects & 0x00ff)
			{
				if(iSects & 0x02)
					xl = Math.max(x, aBndZ[y]);
				else
					xl = x;
				if(iSects & 0x01)
					xr = Math.min(xrDef, aBndA[y]);
				else
					xr = xrDef;
				y2 = cy - y - h;
				w = xr - xl;
				if(w > 0) this.mkDiv(xl, y2, w, h);
			}
			if(iSects & 0xff00)
			{
				if(iSects & 0x0100)
					xl = Math.max(x, aBndA[y]);
				else
					xl = x;
				if(iSects & 0x0200)
					xr = Math.min(xrDef, aBndZ[y]);
				else
					xr = xrDef;
				y2 = cy + y + (iOdds >> 16);
				w = xr - xl;
				if(w > 0) this.mkDiv(xl, y2, w, h);
			}
		}
	};

	this.mkOvQds = function(cx, cy, xl, xr, yt, yb, w, h)
	{
		this.mkDiv(xr+cx, yt+cy, w, h);
		this.mkDiv(xr+cx, yb+cy, w, h);
		this.mkDiv(xl+cx, yb+cy, w, h);
		this.mkDiv(xl+cx, yt+cy, w, h);
	};
	
	this.setStroke(1);
	this.setFont('verdana,geneva,helvetica,sans-serif', String.fromCharCode(0x31, 0x32), ajax.drawing.Graphics.Font.PLAIN);
	this.color = '#000000';
	this.htm = '';	

	if (!(ajax.drawing.Graphics.jg_ie || ajax.drawing.Graphics.jg_dom || ajax.drawing.Graphics.jg_ihtm)) ajax.drawing.Graphics.chkDHTM(this.doc);
	
	if (typeof id == "undefined" || !id) // No Id so render to Doc
	    this.repaint = ajax.drawing.Graphics.pntDoc;
	else
	{
	    if ( typeof(id) == "object") // Render to element handle
	        this.cnv = id;
	    else
	        // Grab element handle to render to
		    this.cnv = this.doc.all? (this.doc.all[id] || null)
			    : this.doc.getElementById? (this.doc.getElementById(id) || null)
			    : null;
		    
		this.defhtm = (this.cnv && this.cnv.innerHTML)? this.cnv.innerHTML : '';
		this.repaint = ajax.drawing.Graphics.jg_dom? ajax.drawing.Graphics.pntCnvDom : ajax.drawing.Graphics.jg_ie? ajax.drawing.Graphics.pntCnvIe : ajax.drawing.Graphics.jg_ihtm? ajax.drawing.Graphics.pntCnvIhtm : ajax.drawing.Graphics.pntCnv;
	}

	this.setPrintable(false);
	
	this.hasLoaded = function(){ return 1; } // Always loaded.
}

//-----------------------------------------------------------------------------
ajax.lang.extend("ajax.drawing.Graphics", "ajax.drawing.GraphicsBase"); // Inherit
//-----------------------------------------------------------------------------

ajax.drawing.Graphics.prototype.toString = function()
{
    return "ajax.drawing.Graphics";
}

//-----------------------------------------------------------------------------
ajax.drawing.Graphics.prototype.setStroke = function(x)
{
	this.stroke = x;
	if (!(x+1))
	{
		this.drawLine   = ajax.drawing.Graphics.mkLinDott;
		this.mkOv       = ajax.drawing.Graphics.mkOvDott;
		this.drawRect   = ajax.drawing.Graphics.mkRectDott;
	}
	else if (x-1 > 0)
	{
		this.drawLine   = ajax.drawing.Graphics.mkLin2D;
		this.mkOv       = ajax.drawing.Graphics.mkOv2D;
		this.drawRect   = ajax.drawing.Graphics.mkRect;
	}
	else
	{
		this.drawLine   = ajax.drawing.Graphics.mkLin;
		this.mkOv       = ajax.drawing.Graphics.mkOv;
		this.drawRect   = ajax.drawing.Graphics.mkRect;
	}
};

//-----------------------------------------------------------------------------
ajax.drawing.Graphics.cast = function(obj)
{///<returns type="ajax.drawing.Graphics"/>
    return obj;    
}

//-----------------------------------------------------------------------------
/*
 The following methods are used as function pointers (so code branching is only done once)
 so do not appear as part of a class instance directly but are called in as required
 hence the method definitions sit outside the class prototype, like a static method
 but still reference the "this" reference.
 
 For example, the class code might have something like..
 
 this.drawLine = (this.isIe) ? ajax.drawing.Graphics.mkLin : ajax.drawing.Graphics.mkLinNS
 
 .. then later simply call the drawLine() method to draw correctly for that browser.
*/


//-----------------------------------------------------------------------------
// Line variants
ajax.drawing.Graphics.mkLin = function(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	if (dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while ((dx--) > 0)
		{
			++x;
			if (p > 0)
			{
				this.mkDiv(ox, y, x-ox, 1);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this.mkDiv(ox, y, x2-ox+1, 1);
	}

	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if (y2 <= y1)
		{
			while ((dy--) > 0)
			{
				if (p > 0)
				{
					this.mkDiv(x++, y, 1, oy-y+1);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this.mkDiv(x2, y2, 1, oy-y2+1);
		}
		else
		{
			while ((dy--) > 0)
			{
				y += yIncr;
				if (p > 0)
				{
					this.mkDiv(x++, oy, 1, y-oy);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this.mkDiv(x2, oy, 1, y2-oy+1);
		}
	}
}

ajax.drawing.Graphics.mkLin2D = function(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	var s = this.stroke;
	if (dx >= dy)
	{
		if (dx > 0 && s-3 > 0)
		{
			var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.ceil(s/2);

		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while ((dx--) > 0)
		{
			++x;
			if (p > 0)
			{
				this.mkDiv(ox, y, x-ox+ad, _s);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this.mkDiv(ox, y, x2-ox+ad+1, _s);
	}

	else
	{
		if (s-3 > 0)
		{
			var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.round(s/2);

		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if (y2 <= y1)
		{
			++ad;
			while ((dy--) > 0)
			{
				if (p > 0)
				{
					this.mkDiv(x++, y, _s, oy-y+ad);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this.mkDiv(x2, y2, _s, oy-y2+ad);
		}
		else
		{
			while ((dy--) > 0)
			{
				y += yIncr;
				if (p > 0)
				{
					this.mkDiv(x++, oy, _s, y-oy+ad);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this.mkDiv(x2, oy, _s, y2-oy+ad+1);
		}
	}
}

ajax.drawing.Graphics.mkLinDott = function(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1,
	drw = true;
	if (dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx;
		while ((dx--) > 0)
		{
			if (drw) this.mkDiv(x, y, 1, 1);
			drw = !drw;
			if (p > 0)
			{
				y += yIncr;
				p += pru;
			}
			else p += pr;
			++x;
		}
		if (drw) this.mkDiv(x, y, 1, 1);
	}

	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy;
		while ((dy--) > 0)
		{
			if (drw) this.mkDiv(x, y, 1, 1);
			drw = !drw;
			y += yIncr;
			if (p > 0)
			{
				++x;
				p += pru;
			}
			else p += pr;
		}
		if (drw) this.mkDiv(x, y, 1, 1);
	}
}

ajax.drawing.Graphics.mkOv = function(left, top, width, height)
{
	var a = width>>1, b = height>>1,
	wod = width&1, hod = (height&1)+1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	ox = 0, oy = b,
	aa = (a*a)<<1, bb = (b*b)<<1,
	st = (aa>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa*((b<<1)-1),
	w, h;
	while (y > 0)
	{
		if (st < 0)
		{
			st += bb*((x<<1)+3);
			tt += (bb<<1)*(++x);
		}
		else if (tt < 0)
		{
			st += bb*((x<<1)+3) - (aa<<1)*(y-1);
			tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
			w = x-ox;
			h = oy-y;
			if (w&2 && h&2)
			{
				this.mkOvQds(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1);
				this.mkOvQds(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1);
			}
			else this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h);
			ox = x;
			oy = y;
		}
		else
		{
			tt -= aa*((y<<1)-3);
			st -= (aa<<1)*(--y);
		}
	}
	this.mkDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod);
	this.mkDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod);
}

ajax.drawing.Graphics.mkOv2D = function(left, top, width, height)
{
	var s = this.stroke;
	width += s-1;
	height += s-1;
	var a = width>>1, b = height>>1,
	wod = width&1, hod = (height&1)+1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa = (a*a)<<1, bb = (b*b)<<1,
	st = (aa>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa*((b<<1)-1);

	if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))
	{
		var ox = 0, oy = b,
		w, h,
		pxl, pxr, pxt, pxb, pxw;
		while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - (aa<<1)*(y-1);
				tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
				w = x-ox;
				h = oy-y;

				if (w-1)
				{
					pxw = w+1+(s&1);
					h = s;
				}
				else if (h-1)
				{
					pxw = s;
					h += 1+(s&1);
				}
				else pxw = h = s;
				this.mkOvQds(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h);
				ox = x;
				oy = y;
			}
			else
			{
				tt -= aa*((y<<1)-3);
				st -= (aa<<1)*(--y);
			}
		}
		this.mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);
		this.mkDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod);
	}

	else
	{
		var _a = (width-((s-1)<<1))>>1,
		_b = (height-((s-1)<<1))>>1,
		_x = 0, _y = _b,
		_aa = (_a*_a)<<1, _bb = (_b*_b)<<1,
		_st = (_aa>>1)*(1-(_b<<1)) + _bb,
		_tt = (_bb>>1) - _aa*((_b<<1)-1),

		pxl = new Array(),
		pxt = new Array(),
		_pxb = new Array();
		pxl[0] = 0;
		pxt[0] = b;
		_pxb[0] = _b-1;
		while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
				pxl[pxl.length] = x;
				pxt[pxt.length] = y;
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - (aa<<1)*(y-1);
				tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
				pxl[pxl.length] = x;
				pxt[pxt.length] = y;
			}
			else
			{
				tt -= aa*((y<<1)-3);
				st -= (aa<<1)*(--y);
			}

			if (_y > 0)
			{
				if (_st < 0)
				{
					_st += _bb*((_x<<1)+3);
					_tt += (_bb<<1)*(++_x);
					_pxb[_pxb.length] = _y-1;
				}
				else if (_tt < 0)
				{
					_st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1);
					_tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3);
					_pxb[_pxb.length] = _y-1;
				}
				else
				{
					_tt -= _aa*((_y<<1)-3);
					_st -= (_aa<<1)*(--_y);
					_pxb[_pxb.length-1]--;
				}
			}
		}

		var ox = 0, oy = b,
		_oy = _pxb[0],
		l = pxl.length,
		w, h;
		for (var i = 0; i < l; i++)
		{
			if (typeof _pxb[i] != "undefined")
			{
				if (_pxb[i] < _oy || pxt[i] < oy)
				{
					x = pxl[i];
					this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy);
					ox = x;
					oy = pxt[i];
					_oy = _pxb[i];
				}
			}
			else
			{
				x = pxl[i];
				this.mkDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod);
				this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
				ox = x;
				oy = pxt[i];
			}
		}
		this.mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);
		this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
	}
}

ajax.drawing.Graphics.mkOvDott = function(left, top, width, height)
{
	var a = width>>1, b = height>>1,
	wod = width&1, hod = height&1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
	st = (aa2>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa2*((b<<1)-1),
	drw = true;
	while (y > 0)
	{
		if (st < 0)
		{
			st += bb*((x<<1)+3);
			tt += (bb<<1)*(++x);
		}
		else if (tt < 0)
		{
			st += bb*((x<<1)+3) - aa4*(y-1);
			tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
		}
		else
		{
			tt -= aa2*((y<<1)-3);
			st -= aa4*(--y);
		}
		if (drw) this.mkOvQds(cx, cy, -x, x+wod, -y, y+hod, 1, 1);
		drw = !drw;
	}
}

ajax.drawing.Graphics.mkRect = function(x, y, w, h)
{
	var s = this.stroke;
	this.mkDiv(x, y, w, s);
	this.mkDiv(x+w, y, s, h);
	this.mkDiv(x, y+h, w+s, s);
	this.mkDiv(x, y+s, s, h-s);
}

ajax.drawing.Graphics.mkRectDott = function(x, y, w, h)
{
	this.drawLine(x, y, x+w, y);
	this.drawLine(x+w, y, x+w, y+h);
	this.drawLine(x, y+h, x+w, y+h);
	this.drawLine(x, y, x, y+h);
}

ajax.drawing.Graphics.mkLinVirt = function(aLin, x1, y1, x2, y2)
{
	var dx = Math.abs(x2-x1), dy = Math.abs(y2-y1),
	x = x1, y = y1,
	xIncr = (x1 > x2)? -1 : 1,
	yIncr = (y1 > y2)? -1 : 1,
	p,
	i = 0;
	if(dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1);
		p = pr-dx;
		while(dx > 0)
		{--dx;
			if(p > 0)    //  Increment y
			{
				aLin[i++] = x;
				y += yIncr;
				p += pru;
			}
			else p += pr;
			x += xIncr;
		}
	}
	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1);
		p = pr-dy;
		while(dy > 0)
		{--dy;
			y += yIncr;
			aLin[i++] = x;
			if(p > 0)    //  Increment x
			{
				x += xIncr;
				p += pru;
			}
			else p += pr;
		}
	}
	for(var len = aLin.length, i = len-i; i;)
		aLin[len-(i--)] = x;
};
//-----------------------------------------------------------------------------



//-----------------------------------------------------------------------------
// pntDoc variants
ajax.drawing.Graphics.pntDoc = function()
{
	this.doc.write(ajax.drawing.Graphics.jg_fast? this.htmRpc() : this.htm);
	this.htm = '';
}


ajax.drawing.Graphics.pntCnvDom = function()
{
	var x = this.doc.createRange();
	x.setStartBefore(this.cnv);
	x = x.createContextualFragment(ajax.drawing.Graphics.jg_fast? this.htmRpc() : this.htm);
	if(this.cnv) this.cnv.appendChild(x);
	this.htm = '';
}


ajax.drawing.Graphics.pntCnvIe = function()
{
	if(this.cnv) this.cnv.insertAdjacentHTML("BeforeEnd", ajax.drawing.Graphics.jg_fast? this.htmRpc() : this.htm);
	this.htm = '';
}


ajax.drawing.Graphics.pntCnvIhtm = function()
{
	if(this.cnv) this.cnv.innerHTML += this.htm;
	this.htm = '';
}


ajax.drawing.Graphics.pntCnv = function()
{
	this.htm = '';
}
//-----------------------------------------------------------------------------



//-----------------------------------------------------------------------------
// mkDiv variants
ajax.drawing.Graphics.mkDiv = function(x, y, w, h)
{
    // Scaling to enable the lowering of the resolution.  
    var scale = this.getScale();    
    x*= scale;
    y*= scale;
    w*= scale;
    h*= scale;
    x -= this.scaleWidth;
    y -= this.scaleHeight; 
    
	this.htm += '<div style="position:absolute;'+
		'left:' + x + 'px;'+
		'top:' + y + 'px;'+
		'width:' + w + 'px;'+
		'height:' + h + 'px;'+
		'clip:rect(0,'+w+'px,'+h+'px,0);'+
		'background-color:' + this.color +
		(!ajax.drawing.Graphics.jg_moz? ';overflow:hidden' : '')+
		';"><\/div>';
}

ajax.drawing.Graphics.mkDivIe = function(x, y, w, h)
{    
    // PolyFill Caller will cull on y axis.    
    // height will always be 1 - so we only need to worry about the width
    if (x < 0 || x > this.scaleAdjustedWidth)
        return;
    
    w = Math.max(Math.min(this.scaleAdjustedWidth - x, w), 0);
    
    // Scaling to enable the lowering of the resolution.  
    var scale = this.getScale();    
    x*= scale;
    y*= scale;
    w*= scale;
    h*= scale;
    x -= this.scaleWidth;
    y -= this.scaleHeight;   

	this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';
}

ajax.drawing.Graphics.mkDivPrt = function(x, y, w, h)
{
	this.htm += '<div style="position:absolute;'+
		'border-left:' + w + 'px solid ' + this.color + ';'+
		'left:' + x + 'px;'+
		'top:' + y + 'px;'+
		'width:0px;'+
		'height:' + h + 'px;'+
		'clip:rect(0,'+w+'px,'+h+'px,0);'+
		'background-color:' + this.color +
		(!ajax.drawing.Graphics.jg_moz? ';overflow:hidden' : '')+
		';"><\/div>';
}

ajax.drawing.Graphics.mkLyr = function(x, y, w, h)
{
	this.htm += '<layer '+
		'left="' + x + '" '+
		'top="' + y + '" '+
		'width="' + w + '" '+
		'height="' + h + '" '+
		'bgcolor="' + this.color + '"><\/layer>\n';
}
//-----------------------------------------------------------------------------




//-----------------------------------------------------------------------------
// Replacements
ajax.drawing.Graphics.regex =  /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
ajax.drawing.Graphics.htmRpc = function()
{
	return this.htm.replace(
		ajax.drawing.Graphics.regex,
		'<div style="overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2;top:$3;width:$4;height:$5"></div>\n');
}

ajax.drawing.Graphics.htmPrtRpc = function()
{
	return this.htm.replace(
		ajax.drawing.Graphics.regex,
		'<div style="overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1"></div>\n');
}





//-----------------------------------------------------------------------------
// Create namespaced Graphics Font class and instance...
ajax.drawing.Graphics.jsgFont = function()
{
	this.PLAIN = 'font-weight:normal;';
	this.BOLD = 'font-weight:bold;';
	this.ITALIC = 'font-style:italic;';
	this.ITALIC_BOLD = this.ITALIC + this.BOLD;
	this.BOLD_ITALIC = this.ITALIC_BOLD;
}
ajax.drawing.Graphics.Font = new ajax.drawing.Graphics.jsgFont();


// Unreferenced code?
ajax.drawing.Graphics.jsgStroke = function()
{
	this.DOTTED = -1;
}
ajax.drawing.Graphics.Stroke = new ajax.drawing.Graphics.jsgStroke();
//-----------------------------------------------------------------------------













//-----------------------------------------------------------------------------
// Static Global functions, these are not set to internal pointers but just called statically
// by class instances.
ajax.drawing.Graphics.jg_ihtm   = false;
ajax.drawing.Graphics.jg_ie     = false;
ajax.drawing.Graphics.jg_fast   = false;
ajax.drawing.Graphics.jg_dom    = false;
ajax.drawing.Graphics.jg_moz    = false;
ajax.drawing.Graphics.jg_n4     = false;

//-----------------------------------------------------------------------------
// This method initializes the variables above.
ajax.drawing.Graphics.chkDHTM = function(doc, x, i)
{
	x = doc.body || null;
	ajax.drawing.Graphics.jg_ie = x && typeof x.insertAdjacentHTML != "undefined";
	ajax.drawing.Graphics.jg_dom = (x && !ajax.drawing.Graphics.jg_ie &&
		typeof x.appendChild != "undefined" &&
		typeof doc.createRange != "undefined" &&
		typeof (i = doc.createRange()).setStartBefore != "undefined" &&
		typeof i.createContextualFragment != "undefined");
	ajax.drawing.Graphics.jg_ihtm = !ajax.drawing.Graphics.jg_ie && !ajax.drawing.Graphics.jg_dom && x && typeof x.innerHTML != "undefined";
	ajax.drawing.Graphics.jg_fast = ajax.drawing.Graphics.jg_ie && doc.all && !window.opera;
	ajax.drawing.Graphics.jg_moz = ajax.drawing.Graphics.jg_dom && typeof x.style.MozOpacity != "undefined";
	ajax.drawing.Graphics.jg_n4     = (doc.layers && typeof doc.classes != "undefined");
}

//-----------------------------------------------------------------------------
ajax.drawing.Graphics.integer_compare = function(x,y)
{
	return (x < y) ? -1 : ((x > y)*1);
}



/* This notice must be untouched at all times.

wz_jsgraphics.js    v. 2.36
The latest version is available at
http://www.walterzorn.com
or http://www.devira.com
or http://www.walterzorn.de

Copyright (c) 2002-2004 Walter Zorn. All rights reserved.
Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
Last modified: 21. 6. 2006

Performance optimizations for Internet Explorer
by Thomas Frank and John Holdsworth.
fillPolygon method implemented by Matthieu Haller.

High Performance JavaScript Graphics Library.
Provides methods
- to draw lines, rectangles, ellipses, polygons
	with specifiable line thickness,
- to fill rectangles and ellipses
- to draw text.
NOTE: Operations, functions and branching have rather been optimized
to efficiency and speed than to shortness of source code.

LICENSE: LGPL

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
or see http://www.gnu.org/copyleft/lesser.html
*/